package db

import (
	"fmt"
	"strconv"
	"time"
	"github.com/garyburd/redigo/redis"
	log "github.com/sirupsen/logrus"
)

type Redis struct {
	Pool     *redis.Pool
	Server   string
	Host     string
	Port     int
	Prefix   string
}

func NewRedis(host string, port int, args ...interface{}) *Redis{

	model  := new(Redis)

	model.Host         = host

	model.Port         = port

	model.Server       = fmt.Sprintf("%s:%d", host, port)

	err               := model.init()

	if err != nil {
		log.Errorln("[redis: %s]: error [%s].", model.Server, err.Error())
		panic(err)
	}
	log.Infof("[redis: %s]: success", model.Server)

	return model
}

func (this *Redis) Test() {
	log.Info("test")
}

func (this *Redis) init() error {
	this.Pool = &redis.Pool{
		MaxIdle:     64,
		IdleTimeout: 60 * time.Second,
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", this.Server)
			if err != nil {
				return nil, err
			}
			//_, err = c.Do("SELECT", config.RedisDb)
			return c, err
		},
	}
	return nil
}

// 写入值永不过期
func (this *Redis) SetString(key, value string) error {
	conn := this.Pool.Get()
	defer conn.Close()
	_, err := conn.Do("SET", key, value)
	if err != nil {
		//fmt.Println("redis set failed:", err)
	}
	return err
}

func (this *Redis) SetExString(key, value string, expireTime int /* seconds*/) error {
	conn := this.Pool.Get()
	defer conn.Close()
	expStr := strconv.Itoa(expireTime)
	_, err := conn.Do("SETEX", key, expStr, value)
	if err != nil {
		//fmt.Println("redis set failed:", err)
	}
	return err
}

func (this *Redis) Delete(key string) error {
	conn := this.Pool.Get()
	defer conn.Close()
	_, err := conn.Do("DEL", key)
	if err != nil {
		//fmt.Println("redis set failed:", err)
	}
	return err
}

func (this *Redis) Expire(key string, expireTime int /* seconds*/) error {
	conn := this.Pool.Get()
	defer conn.Close()
	expStr := strconv.Itoa(expireTime)
	_, err := conn.Do("EXPIRE", key, expStr)
	if err != nil {
		//fmt.Println("redis set failed:", err)
	}
	return err
}

func (this *Redis) GetString(key string) (string, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	value, err := redis.String(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (this *Redis) GetInt64(key string) (int64, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	value, err := redis.Int64(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (this *Redis) GetUint64(key string) (uint64, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	value, err := redis.Uint64(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (this *Redis) GetInt(key string) (int, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	value, err := redis.Int(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (this *Redis) GetBool(key string) (bool, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	value, err := redis.Bool(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (this *Redis) Exists(key string) (bool, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	value, err := redis.Bool(conn.Do("EXISTS", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (this *Redis) HMSet(key string, data interface{}) error {
	conn := this.Pool.Get()
	defer conn.Close()
	if _, err := conn.Do("HMSET", redis.Args{}.Add(key).AddFlat(data)...); err != nil {
		//fmt.Println("redis set failed:", err)
		return err
	}

	return nil
}

func (this *Redis) HMGet(key string, keys interface{}) ([]string, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	v, err := redis.Strings(conn.Do("HMGET", redis.Args{}.Add(key).AddFlat(keys)...))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return v, err
	}
	// if err := redis.ScanStruct(v, data); err != nil {
	// 	return err
	// }
	return v, nil
}

func (this *Redis) HSet(key, field, value string) error {
	conn := this.Pool.Get()
	defer conn.Close()
	if _, err := conn.Do("HSET", key, field, value); err != nil {
		//fmt.Println("redis set failed:", err)
		return err
	}

	return nil
}

func (this *Redis) HDel(key, field string) error {
	conn := this.Pool.Get()
	defer conn.Close()
	if _, err := conn.Do("HDEL", key, field); err != nil {
		return err
	}

	return nil
}

func (this *Redis) HGetAll(key string, data interface{}) error {
	conn := this.Pool.Get()
	defer conn.Close()
	v, err := redis.Values(conn.Do("HGETALL", key))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return err
	}
	if err := redis.ScanStruct(v, data); err != nil {
		return err
	}
	return nil
}

func (this *Redis) HGet(key string, field string) (string, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	value, err := redis.String(conn.Do("HGET", key, field))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return "", err
	}
	return value, nil
}

func (this *Redis) LPop(listName string) (string, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	value, err := redis.String(conn.Do("LPOP", listName))
	if err != nil {
		//fmt.Println("LPop redis get failed:", err)
	}
	return value, err
}

func (this *Redis) LRem(listName string, count int, value string) error {
	conn := this.Pool.Get()
	defer conn.Close()
	cntStr := strconv.Itoa(count)
	_, err := redis.Int(conn.Do("LREM", listName, cntStr, value))
	if err != nil {
		//fmt.Println("LPop redis get failed:", err)
	}
	return err
}

func (this *Redis) RPush(listName string, element string) error {
	conn := this.Pool.Get()
	defer conn.Close()
	_, err := redis.Int(conn.Do("RPUSH", listName, element))
	if err != nil {
		//fmt.Println("RPush redis get failed:", err)
	}
	return err
}

func (this *Redis) LLen(listName string) (int, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	value, err := redis.Int(conn.Do("LLEN", listName))
	if err != nil {
		//fmt.Println("LLEN redis get failed:", err)
	}
	return value, err
}

func (this *Redis) LIndex(listName string, index int) (string, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	idxStr := strconv.Itoa(index)
	value, err := redis.String(conn.Do("LINDEX", listName, idxStr))
	if err != nil {
		//fmt.Println("LIndex redis get failed:", err)
	}
	return value, err
}

func (this *Redis) Incr(key string) (uint64, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	value, err := redis.Uint64(conn.Do("INCR", key))
	if err != nil {
		//fmt.Println("RPush redis get failed:", err)
	}
	return value, err
}

func (this *Redis) SAdd(key string, uid string) error {
	conn := this.Pool.Get()
	defer conn.Close()
	if _, err := conn.Do("SADD", key, uid); err != nil {
		return err
	}
	return nil
}

func (this *Redis) SRem(key string, uid string) error {
	conn := this.Pool.Get()
	defer conn.Close()
	if _, err := conn.Do("SREM", key, uid); err != nil {
		return err
	}
	return nil
}

func (this *Redis) atoi64(s string) int64 {
	if s == "" {
		return 0
	}

	i, err := strconv.ParseInt(s, 10, 0)

	if err != nil {
		return 0
	}
	return i
}

func (this *Redis) SMembers(key string) ([]uint64, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	v, err := redis.Strings(conn.Do("SMEMBERS", key))
	if err != nil {
		var uids []uint64
		return uids, err
	}
	uids := make([]uint64, len(v))
	for i, s := range v {
		uids[i] = uint64(this.atoi64(s))
	}
	return uids, nil
}

func (this *Redis) IncrBy(key string, incr int) (uint64, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	value, err := redis.Uint64(conn.Do("INCRBY", key, incr))
	if err != nil {
		fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (this *Redis) ZAdd(key, score, member string) error {
	conn := this.Pool.Get()
	defer conn.Close()
	if _, err := conn.Do("ZADD", key, score, member); err != nil {
		return err
	}
	return nil
}

func (this *Redis) ZAdds(key string, data interface{}) error {
	conn := this.Pool.Get()
	defer conn.Close()
	if _, err := conn.Do("ZADD", redis.Args{}.Add(key).AddFlat(data)...); err != nil {
		return err
	}
	return nil
}
// func (this *Redis) ZRange(key string) ([]string, error) {
// 	conn := this.Pool.Get()
// 	defer conn.Close()
// 	v, err := redis.Strings(conn.Do("ZRANGE", key, 0, -1))
// 	if err != nil {
// 		var chunks []string
// 		return chunks, err
// 	}
// 	return v, nil
// }

func (this *Redis) ZRange(key string, start int, end int) ([]string, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	v, err := redis.Strings(conn.Do("ZRANGE", key, start, end))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return v, err
	}
	// if err := redis.ScanStruct(v, data); err != nil {
	// 	return err
	// }
	return v, nil
}

func (this *Redis) ZREVRange(key string, start int, end int) ([]string, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	v, err := redis.Strings(conn.Do("ZREVRANGE", key, start, end))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return v, err
	}
	// if err := redis.ScanStruct(v, data); err != nil {
	// 	return err
	// }
	return v, nil
}

func (this *Redis) ZRANGEBYSCORE(key string, start int, end int) ([]string, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	v, err := redis.Strings(conn.Do("ZRANGEBYSCORE", key, start, end))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return v, err
	}
	// if err := redis.ScanStruct(v, data); err != nil {
	// 	return err
	// }
	return v, nil
}

func (this *Redis) ZRANGEBYSCORE2(key string, start int, end int, offset int, count int) ([]string, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	v, err := redis.Strings(conn.Do("ZRANGEBYSCORE", key, start, end, "LIMIT", offset, count))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return v, err
	}
	// if err := redis.ScanStruct(v, data); err != nil {
	// 	return err
	// }
	return v, nil
}

func (this *Redis) ZREVRANGEBYSCORE(key string, start int, end int) ([]string, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	v, err := redis.Strings(conn.Do("ZREVRANGEBYSCORE", key, start, end))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return v, err
	}
	// if err := redis.ScanStruct(v, data); err != nil {
	// 	return err
	// }
	return v, nil
}

func (this *Redis) ZREVRANGEBYSCORE2(key string, start int, end int, offset int, count int) ([]string, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	v, err := redis.Strings(conn.Do("ZREVRANGEBYSCORE", key, start, end, "LIMIT", offset, count))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return v, err
	}
	// if err := redis.ScanStruct(v, data); err != nil {
	// 	return err
	// }
	return v, nil
}


func (this *Redis) ZCARD(listName string) (int, error) {
	conn := this.Pool.Get()
	defer conn.Close()
	value, err := redis.Int(conn.Do("ZCARD", listName))
	if err != nil {
		//fmt.Println("ZCARD redis get failed:", err)
	}
	return value, err
}